﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Reflection.Emit;

using Scala.Compiler.Ast;
using Scala.Compiler.Binding;
using Microsoft.Scripting.Utils;

namespace Scala.Compiler.Types
{
    public static class NewTypeMaker
    {
        public static void CreatePackage(Tree tree, string packageString, ModuleBuilder modBldr)
        {
            if (!(tree is PackageDefStatement)) throw new Exception("The input for creating Package is invalid");
            var pack = tree as PackageDefStatement;
            if (pack.Statements[0] is PackageDefStatement)
            {
                CreatePackage(pack.Statements[0], packageString + "." + pack.PId, modBldr);
            }
            else
            {
                foreach (var stat in pack.Statements)
                {
                    if(stat is ModuleDefStatement || stat is ClassDefStatement)
                    {
                        CreateType(stat, packageString, modBldr);
                    }
                    else
                    {
                        throw new Exception("The input for creating Type is invalid");
                    }
                }
            }
        }

        public static Type CreateType(Tree tree, string packageString, ModuleBuilder modBldr)
        {
            if (!(tree is ModuleDefStatement) && (tree is ClassDefStatement)) throw new Exception("The input for creating Type is invalid");
            var scModule = tree as ModuleDefStatement;
            var scClass = tree as ClassDefStatement;

            ScalaType scalaType = scModule == null ? scClass.ScalaType : scModule.ScalaType;
            TypeBuilder newType = modBldr.DefineType(scalaType.Name, scalaType.GetAttributes());

            return null;
        }

        public static void CreateField(TypeBuilder newType, string name, FieldAttributes attrs, Type fieldType)
        {
            FieldBuilder field = newType.DefineField(name, fieldType, attrs);
        }

        public static void CreateMethod(TypeBuilder newType, Tree tree)
        {
            MethodBuilder meth;
        }

        public static PropertyBuilder CreateProperty(TypeBuilder newType, string name, Type propType, PropertyAttributes attrs)
        {
            ContractUtils.RequiresNotNull(newType, "newType");
            ContractUtils.RequiresNotNull(propType, "propType");
            ContractUtils.RequiresNotNull(attrs, "attrs");

            FieldBuilder field = newType.DefineField("__" + name, propType, FieldAttributes.Private);
            PropertyBuilder prop = newType.DefineProperty(name, attrs, propType, null);

            //Getter
            MethodBuilder getMeth = newType.DefineMethod("get__" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propType, Type.EmptyTypes);
            ILGenerator getIL = getMeth.GetILGenerator();

            getIL.Emit(OpCodes.Ldarg_0);
            getIL.Emit(OpCodes.Ldfld, field);
            getIL.Emit(OpCodes.Ret);

            //Setter
            MethodBuilder setMeth = newType.DefineMethod("set__" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propType });
            ILGenerator setIL = setMeth.GetILGenerator();

            setIL.Emit(OpCodes.Ldarg_0);
            setIL.Emit(OpCodes.Ldarg_1);
            setIL.Emit(OpCodes.Stfld, field);
            setIL.Emit(OpCodes.Ret);

            prop.SetGetMethod(getMeth);
            prop.SetSetMethod(setMeth);
            return prop;
        }
    }
}
